//
//  BusInterface.cpp
//  sad
//
//  Created by Christopher Kemsley on 2/23/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "BusInterface.hpp"
#include "Log.hpp"
#include "Exception.hpp"
#include "ReentyBlock.hpp"
#include <map>




BusNamespaceBegin

unsigned	requestUniqueID		=	1 ;


std::ostream& operator<< ( std::ostream & stream , Request const & request )
	{
	stream << "<Request" ;
	stream << " mode=" << ( (request.operation==Bus::kOperationRead) ? "Read" : "Write" ) ;
	stream << " address=" << request.baseAddress ;
	stream << " length=" << request.blockLength ;
	return stream << ">" ;
	}





Address					gUniqueAddressIndex = 1 ;
std::map<void*,Address>	gUniqueAddressMap ;

Address		uniqueAddress	( void* pointer )
	{
	std::map<void*,Address>::iterator	found	=	gUniqueAddressMap.find(pointer) ;
	if ( found == gUniqueAddressMap.end() )
		{
		return gUniqueAddressMap[pointer] = (gUniqueAddressIndex++) ;
		}
	else
		{
		return found -> second ;
		}
	}





Request :: Request		()
:	baseAddress	( 0 )
,	operation	( 0 )
,	blockLength	( 0 )
,	uniqueID	( 0 )
	{
	
	}
Request :: Request		( Request const & copy )
:	baseAddress	( copy.baseAddress )
,	operation	( copy.operation )
,	blockLength	( copy.blockLength )
,	uniqueID	( copy.uniqueID )
	{
	
	}
Request :: Request		( Address const & inAddress , Operation const & inOperation , Length const & inBlockLength )
:	baseAddress	( inAddress )
,	operation	( inOperation )
,	blockLength	( inBlockLength )
,	uniqueID	( requestUniqueID++ )
	{
	if ( inBlockLength == 0 )
		ThrowException ( "Request" , "Request cannot have 0-length" , __FILE__,__LINE__ ) ;
	if ( inOperation!=kOperationRead && inOperation!=kOperationWrite )
		ThrowException ( "Request" , "Request has invalid operation" , __FILE__,__LINE__ ) ;
	}
Request :: ~Request	()
	{
	
	}


bool		Request :: isValid () const
	{
	if ( operation!=kOperationRead && operation!=kOperationWrite )
		return false ;
	if ( blockLength == 0 )
		return false ;
	return true ;
	}
void		Request :: invalidate ()
	{
	baseAddress	=	0 ;
	operation	=	0 ;
	blockLength	=	0 ;
	uniqueID	=	0 ;
	}

void		Request :: operator= ( Request const & copy )
	{
	baseAddress	=	copy.baseAddress ;
	operation	=	copy.operation ;
	blockLength	=	copy.blockLength ;
	uniqueID	=	copy.uniqueID ;
	
	if ( ! isValid() )
		ThrowException ( "Request" , "Request set to invalid request" , __FILE__,__LINE__ ) ;
	}
bool		Request :: operator== ( Request const & compare ) const
	{
	return compare.uniqueID == uniqueID ;
	}









Pipe :: Pipe ()
:	mValue ()
,	mIsValid ( false )
,	mWasRead ()
,	mWasWritten ()
,	mReaderCount(0)
,	mWriterCount(0)
	{
	
	}
Pipe :: ~Pipe ()
	{
	
	}

void	Pipe :: read	( Word & word )
	{
	//ReentyBlock r ( "Pipe.read" ) ;
	
	Log::states("Pipe") << MethodId(Pipe,read) << "Called" ;
	
	// Open the reading session
	if ( mReaderCount )
		ThrowException ( "Pipe" , "Somebody started reading, but already reading!" , __FILE__,__LINE__ ) ;
	mReaderCount ++ ;
	
	// Wait for it to be valid
	if ( ! mIsValid )
		wait ( mWasWritten ) ;
	//if ( ! mIsValid )
	//	ThrowException ( "Pipe" , "-read called, but value was already read" , __FILE__,__LINE__ ) ;
	
	Log::states("Pipe") << MethodId(Pipe,read) << "Done waiting" ;
	
	// Read it
	word = mValue ;
	
	// Record that we read it
	mIsValid = false ;
	
	// Close the reading session
	mReaderCount -- ;
	
	// Tell the writer we're done
	mWasRead . notify ( 0 , kSCIntervalSeconds ) ;
	}
void	Pipe :: write	( Word const & word )
	{
	ReentyBlock r ( "Pipe.write" ) ;
	
	Log::states("Pipe") << MethodId(Pipe,write) << "Called" ;
	
	// Open the writing session
	if ( mWriterCount )
		ThrowException ( "Pipe" , "Somebody started writing, but already writing!" , __FILE__,__LINE__ ) ;
	mWriterCount ++ ;
	
	// Check for double-write
	if ( mIsValid )
		ThrowException ( "Pipe" , "-write called, but value is still unread" , __FILE__,__LINE__ ) ;
	
	// Write the value
	mValue = word ;
	
	// Record that we wrote it
	mIsValid = true ;
	
	// Tell the reader that we wrote it
	mWasWritten . notify ( 0 , kSCIntervalSeconds ) ;
	
	// Wait for them to finish reading it
	wait ( mWasRead ) ;
	
	// Close the reading session
	mWriterCount -- ;
	
	Log::states("Pipe") << MethodId(Pipe,write) << "Done waiting" ;
	}





BusNamespaceEnd

